#ifndef INT64_C 
#define INT64_C(c) (c ## LL) 
#define UINT64_C(c) (c ## ULL) 
#endif 

extern "C" {
	/*Include ffmpeg header file*/
#include <libavformat/avformat.h> 
#include <libavcodec/avcodec.h> 
#include <libswscale/swscale.h> 

#include <libavutil/imgutils.h>  
#include <libavutil/opt.h>     
#include <libavutil/mathematics.h>   
#include <libavutil/samplefmt.h>
}

#include <iostream>
using namespace std;

int main(void)
{
	AVFormatContext* ifmt_ctx = NULL, *ofmt_ctx = NULL;
	const char* in_filename, *out_filename;
	in_filename = "rtmp://58.200.131.2:1935/livetv/hunantv";
	out_filename = "output.flv";

	avformat_network_init();

	AVDictionary* avdic = NULL;
	char option_key[] = "rtsp_transport";
	char option_value[] = "tcp";
	av_dict_set(&avdic, option_key, option_value, 0);
	char option_key2[] = "max_delay";
	char option_value2[] = "5000000";
	av_dict_set(&avdic, option_key2, option_value2, 0);

	AVPacket pkt;
	AVOutputFormat* ofmt = NULL;
	int video_index = -1;
	int frame_index = 0;

	int i;

	//打开输入流
	int ret;
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, &avdic)) < 0)
	{
		cout << "Could not open input file." << endl;
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
	{
		cout << "Failed to retrieve input stream information" << endl;
		goto end;
	}


	//nb_streams代表有几路流

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			//视频流
			video_index = i;
			cout << "get videostream." << endl;
			break;
		}
	}

	av_dump_format(ifmt_ctx, 0, in_filename, 0);

	//打开输出流
	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);

	if (!ofmt_ctx)
	{
		printf("Could not create output context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}
	ofmt = ofmt_ctx->oformat;

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		AVStream* in_stream = ifmt_ctx->streams[i];
		AVStream* out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);

		if (!out_stream)
		{
			printf("Failed allocating output stream.\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}

		//将输出流的编码信息复制到输入流
		ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
		if (ret < 0)
		{
			printf("Failed to copy context from input to output stream codec context\n");
			goto end;
		}

		out_stream->codec->codec_tag = 0;

		if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
			out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	}

	av_dump_format(ofmt_ctx, 0, out_filename, 1);

	if (!(ofmt->flags & AVFMT_NOFILE))
	{
		ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
		if (ret < 0)
		{
			printf("Could not open output URL '%s'", out_filename);
			goto end;
		}
	}

	//写文件头到输出文件
	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0)
	{
		printf("Error occured when opening output URL\n");
		goto end;
	}


	//while循环中持续获取数据包，不管音频视频都存入文件
	while (1)
	{
		AVStream* in_stream, *out_stream;
		//从输入流获取一个数据包
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
			break;

		in_stream = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];
		//copy packet
		//转换 PTS/DTS 时序
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
		//printf("pts %d dts %d base %d\n",pkt.pts,pkt.dts, in_stream->time_base);
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;

		//此while循环中并非所有packet都是视频帧，当收到视频帧时记录一下
		if (pkt.stream_index == video_index)
		{
			printf("Receive %8d video frames from input URL\n", frame_index);
			frame_index++;
		}

		//将包数据写入到文件。
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
		if (ret < 0)
		{
			if (ret == -22) {
				continue;
			}
			else {
				printf("Error muxing packet.error code %d\n", ret);
				break;
			}

		}

		//av_free_packet(&pkt); //此句在新版本中已deprecated 由av_packet_unref代替
		av_packet_unref(&pkt);
	}


	//写文件尾
	av_write_trailer(ofmt_ctx);

end:
	av_dict_free(&avdic);
	avformat_close_input(&ifmt_ctx);
	//Close input
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF)
	{
		cout << "Error occured." << endl;
		return -1;
	}

	return 0;
}
